﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using ModbusLib.Base;

namespace ModbusLib
{
    public class ModbusSocket : ModbusMaster
    {
        public string IP { get; set; }
        public int Port { get; set; }
        public int ReadTimeout { get; set; } = 2000;
        public int ReadBufferSize { get; set; } = 4096;

        private Socket _socket;
        private CancellationTokenSource cts = new CancellationTokenSource();

        protected ModbusSocket(ProtocolType protocolType)
        {
            if (protocolType == ProtocolType.Tcp)
            {
                _socket = new Socket(SocketType.Stream, protocolType);
            }
            else if (protocolType == ProtocolType.Udp)
            {
                _socket = new Socket(SocketType.Dgram, protocolType);
            }
        }

        public bool Connected
        {
            get
            {
                if (_socket == null)
                    throw new Exception("网口对象未初始化");
                if (!_socket.Connected || (_socket.Poll(500,SelectMode.SelectRead) && _socket.Available == 0))
                {
                    return false;
                }
                return true;
            }
        }

        public override void Connect()
        {
            _socket.ReceiveTimeout = ReadTimeout;
            _socket.ReceiveBufferSize = ReadBufferSize;
            _socket.Connect(IP, Port);

            // 这里可以启动一个后台线程进行队列数据的获取和请求
            Task.Run(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    if (!_socket.Connected || (_socket.Poll(200,SelectMode.SelectRead) && _socket.Available == 0)) continue;

                    var model = AsyncModels.FirstOrDefault();
                    if (model == null)
                    {
                        await Task.Delay(100);
                        continue;
                    }

                    Exception _ex = null;
                    byte[] datas = null;
                    try
                    {
                        datas = this.SendAndReceive(model.ReqBytes, model.RespLen);
                    }
                    catch (Exception ex)
                    {
                        _ex = ex;
                    }
                    finally
                    {
                        // 执行完成时，将结果通过委托方法传出
                        model.Completed?.Invoke(datas, model.Handler, _ex);
                        AsyncModels.RemoveAll(id => id.Handler == model.Handler);
                    }
                }
            });
        }

        public override void Disconnect()
        {
            _socket?.Shutdown(SocketShutdown.Both);
            _socket?.Close();
            _socket?.Dispose();
        }
     
        protected override byte[] SendAndReceive(byte[] bytes, int len)
        {
            _socket.Send(bytes, 0, bytes.Length, SocketFlags.None);

            byte[] resp = new byte[6];
            _socket.Receive(resp, 0, 6, SocketFlags.None);

            // 判断TID是否一致
            if (resp[0] != bytes[0] || resp[1] != bytes[1])
            {
                // 清除缓冲区    优化
                _socket.Receive(new byte[_socket.ReceiveBufferSize]);
                throw new Exception("TransactionID不匹配，接收数据无效");
            }

            len = resp[4] * 256 + resp[5];
            resp = new byte[len];
            int count = _socket.Receive(resp, 0, len, SocketFlags.None);

            return resp;
        }
    }
}
